// HarlinnWindowsTest.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "hwinapplication.h"
#include "hwinform.h"
#include "hwinmenu.h"
#include "hwindatetime.h"
#include "hwingraphics.h"
#include "hwinimaging.h"
#include "hwinperlinnoice.h"
#include "hwinstdctrls.h"

#include <iostream>


#include "AnimatedPoint.h"

using namespace harlinn::windows;

class MyForm : public Form
{
    graphics::Factory factory;
    graphics::WriteFactory writeFactory;
    graphics::WriteTextFormat textFormat;
    graphics::WriteTextLayout textLayout;
    graphics::ControlRenderTarget renderTarget;
    graphics::SolidColorBrush blackBrush;
    graphics::WriteTypography typography;

    
    graphics::LinearGradientBrush backgroundBrush;


    float dpiScaleX;
    float dpiScaleY;
    String text;
    String titleText;
    std::shared_ptr<Timer> timer;
    bool showingTime;
public:
    typedef Form Base;

    MyForm();
protected:
    virtual void DoOnInitialize();
    virtual void DoOnShown();
    virtual void DoOnDestroy(Message& message);
    virtual void DoOnDisplayChange(Message& message);
    virtual void DoOnPaint(Message& message);
    virtual void DoOnSize(Message& message);
private:
    void UpdateScale( );
    void InitializeMenuBar();
};


MyForm::MyForm()
    : Base(),
      factory(D2D1_FACTORY_TYPE_SINGLE_THREADED),
      writeFactory(DWRITE_FACTORY_TYPE_SHARED),
      dpiScaleX(0),dpiScaleY(0),
      titleText(L"Windows Development in C++ - working with menus"),
      showingTime(0)
{
    text = titleText;
    timer = make_component<Timer>();

    timer->OnTick.connect([&](Timer* sender) 
    { 
        if(showingTime)
        {
            DateTime now = DateTime::Now();
            if(now.IsDaylightSavingTime())
            {
                text = now.ToString() + L" Daylight saving time"; 
            }
            else
            {
                text = now.ToString() + L" Standard time"; 
            }
            InvalidateRect(); 
        }
    } );
    timer->SetInterval(TimeSpan::FromMilliseconds(500));
    timer->SetEnabled();

}

void MyForm::DoOnInitialize()
{
    SetText(titleText); // 
    textFormat = writeFactory.CreateTextFormat(L"Gabriola",72);
    textFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
    textFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
    
    typography = writeFactory.CreateTypography();

    DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,1};

    typography.AddFontFeature(fontFeature);

    UpdateScale( );
    InitializeMenuBar();
}


void MyForm::InitializeMenuBar()
{
    auto self = As<MyForm>();
    auto fileNewMenuItem = make_component<TextMenuItem>(self,L"&New");
    auto fileOpenMenuItem = make_component<TextMenuItem>(self,L"&Open");
    auto fileSaveMenuItem = make_component<TextMenuItem>(self,L"&Save");
    auto fileSeparator = make_component<SeparatorMenuItem>(self);
    auto fileExitMenuItem = make_component<TextMenuItem>(self,L"E&xit");

    fileNewMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"New selected"; InvalidateRect(); });
    fileOpenMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"Open selected"; InvalidateRect(); });
    fileSaveMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"Save selected"; InvalidateRect(); });
    fileExitMenuItem->OnClick.connect([&](MenuItem*){ Close(); });

    auto fileSubMenu = make_component<SubMenuItem>(self,L"&File");

    fileSubMenu->Add(fileNewMenuItem);
    fileSubMenu->Add(fileOpenMenuItem);
    fileSubMenu->Add(fileSaveMenuItem);
    fileSubMenu->Add(fileSeparator);
    fileSubMenu->Add(fileExitMenuItem);

    auto editSubMenu = make_component<SubMenuItem>(self,L"&Edit");
    
    auto editCutMenuItem = editSubMenu->AddMenuItem(L"Cu&t");
    auto editCopyMenuItem = editSubMenu->AddMenuItem(L"&Copy");
    auto editPasteMenuItem = editSubMenu->AddMenuItem(L"&Paste");
    editCutMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"Cut selected"; InvalidateRect(); });
    editCopyMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"Copy selected"; InvalidateRect(); });
    editPasteMenuItem->OnClick.connect([&](MenuItem*){ showingTime = false; text = L"Paste selected"; InvalidateRect(); });


    auto viewSubMenu = make_component<SubMenuItem>(self,L"&View");
    auto viewTime = viewSubMenu->AddMenuItem(L"&Time");
    viewTime->OnClick.connect([&](MenuItem*)
    {   
        showingTime = true; 
        DateTime now = DateTime::Now();
        if(now.IsDaylightSavingTime())
        {
            text = now.ToString() + L" Daylight saving time"; 
        }
        else
        {
            text = now.ToString() + L" Standard time"; 
        }
        InvalidateRect(); 
    });

    auto menuBar = make_component<MenuBar>(self);

    menuBar->Add(fileSubMenu);
    menuBar->Add(editSubMenu);
    menuBar->Add(viewSubMenu);

    SetMenu(menuBar);
}



void MyForm::DoOnShown()
{
    Base::DoOnShown();

    renderTarget = factory.CreateControlRenderTarget(As<Control>());
    blackBrush = renderTarget.CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black));

    D2D1_GRADIENT_STOP gradientStops[2];
    gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::White, 1);
    gradientStops[0].position = 0.0f;
    gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::DeepSkyBlue, 1);
    gradientStops[1].position = 1.0f;


    graphics::GradientStopCollection gradientStopCollection = renderTarget.CreateGradientStopCollection(gradientStops,2,D2D1_GAMMA_2_2,D2D1_EXTEND_MODE_MIRROR);


    backgroundBrush = renderTarget.CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2D1::Point2F(0, 0),D2D1::Point2F(150, 150)),gradientStopCollection);


}
void MyForm::DoOnDestroy(Message& message)
{
    Base::DoOnDestroy(message);
    blackBrush.Reset();
    renderTarget.Reset();
}

void MyForm::DoOnDisplayChange(Message& message)
{
    UpdateScale( );
    InvalidateRect();
}

void MyForm::DoOnPaint(Message& message)
{
    Base::DoOnPaint(message);
    ValidateRect();
    RECT rc = GetClientRect();

    renderTarget.BeginDraw();

    renderTarget.SetTransform(D2D1::IdentityMatrix());
    renderTarget.Clear(D2D1::ColorF(D2D1::ColorF::White));
    
    D2D1_RECT_F layoutRect = D2D1::RectF(rc.top * dpiScaleY,rc.left * dpiScaleX,
        (rc.right - rc.left) * dpiScaleX,(rc.bottom - rc.top) * dpiScaleY);

    renderTarget.FillRectangle(layoutRect, backgroundBrush);


    D2D1_POINT_2F origin = {layoutRect.left,layoutRect.top};


    textLayout = writeFactory.CreateTextLayout(text.c_str(),UINT(text.length()),textFormat,layoutRect.right - layoutRect.left,layoutRect.bottom - layoutRect.top);

    DWRITE_TEXT_RANGE textRange = {0, UINT(text.length())};

    textLayout.SetTypography(typography,textRange);

    auto found = text.IndexOf(L"C++");

    if(found != String::npos)
    {
        textRange.startPosition = UINT(found);
        textRange.length = 3;
        textLayout.SetFontSize(144.0,textRange);
    }


    renderTarget.DrawTextLayout(origin,textLayout,blackBrush);
    
    renderTarget.EndDraw();
}

void MyForm::DoOnSize(Message& message)
{
    Base::DoOnSize(message);
    if(renderTarget)
    {
        D2D1_SIZE_U size;
        size.width = LOWORD(message.lParam);
        size.height = HIWORD(message.lParam);
        renderTarget.Resize(size);
        

    }
}

void MyForm::UpdateScale( )
{
    factory.GetDesktopDpi(dpiScaleX,dpiScaleY);
    dpiScaleX /= 96.0f;
    dpiScaleY /= 96.0f;
}




int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{

    try
    {
	    UNREFERENCED_PARAMETER(hPrevInstance);
	    UNREFERENCED_PARAMETER(lpCmdLine);

        auto application = make_component<Application>();
        auto form = make_control<MyForm>();
        
        auto result = application->Run(form);

        return result;
    }
    catch(std::exception& exc)
    {
        std::cout << exc.what() << std::endl;
    }
    catch(...)
    {
        
    }
    return 0;
}







